package com.vinjogames.network;

import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Message;

import com.vinjogames.TankGame.TankGameActivity;
import com.vinjogames.entities.Game;
import com.vinjogames.util.GameClock;
import com.vinjogames.util.GameConVars;
import com.vinjogames.util.Logger;

public class ConnectionManager {

	private TankGameActivity mActivity;

	private Server mServer;
	private Client mClient;
	private Game mGame;
	private GameClock mGameClock;
	private boolean isConnected = false;
	private int lastMessageHash;
	private int retry = 10;

	private ConnectionManager(){};

	private static class ConnectionManagerHolder {
		public static final ConnectionManager instance = new ConnectionManager();
	}

	public static ConnectionManager getInstance(){
		return ConnectionManagerHolder.instance;
	}

	public void init(TankGameActivity activity){
		mServer = new Server(this);
		
		mClient = new Client();
		mGame = activity.getGame();
		mGameClock = GameClock.getInstance();
	}

	public String startServer(){
		if(isNetworkAvailable()){
			Logger.log("[ConnectionManager] Starting server");
			return mServer.Start();
		}
		else{
			Logger.log("[ConnectionManager] No network available!");
			return null;
		}
	}

	public boolean connect(String ip, int port){
		if(isNetworkAvailable()){
			Logger.log("[ConnectionManager] Starting client");
			return mClient.Connect(ip, port);
		}else{
			Logger.log("[ConnectionManager] No network available!");
			return false;
		}
	}

	public void sendWithConfirmation(String message){
		if(mClient != null && mClient.mConnected){
			lastMessageHash = message.hashCode();
			send(message);
			
			try {
				Thread.sleep(1000);
				if(lastMessageHash == 0){
					Logger.popUp("Last msg confirmed.");
				}
				
//				while(true){
//					if(lastMessageHash == 0){
//						break;
//					}
//					Thread.sleep(1000);
//					if(retry > 0){
//						Logger.log("Retrying last message");
//						send(message);
//						retry--;
//					}
//					if(retry <= 0){
//						retry = 10;
//						lastMessageHash = 0;
//						Logger.popUp("Failed to send last message");
//						break;
//					}
//				}
				
			} catch (InterruptedException e) {
				Logger.log("Exception: " + e.getMessage());
			}
		}

	}

	public void send(JSONObject msg){
		send(msg.toString());
	}
	public void send(String message){
		if(mClient != null && mClient.mConnected){
			mClient.SendMessage(message);
		}
	}

	/*  Game Actions:
	 *  Select Unit
	 *  Move Unit/Stop Unit  (stop unit not passed, just sync unit loc to network player on stop?)
	 *  Buy unit (add unit, send type?)  build turret
	 *  Buy upgrade
	 *  Angle up/down (combine to one change angle)
	 *  Force up/down (combine to one change force)
	 *  Shoot unit (sync angle, force, loc, wind?)
	 *  End Turn (Sync all unit locations and health?)
	 *  Quit
	 */
	public void receive(String msg){
		JSONObject networkAction = null;
		try {
			networkAction = new JSONObject(msg);
			switch(networkAction.getInt("Type")){
			case GameConVars.NETWORK_SELECTUNIT:
				mGame.nextUnit(networkAction);
				break;
			case GameConVars.NETWORK_TURNUNIT:
				mGame.turnAround(networkAction);
				break;
			case GameConVars.NETWORK_MOVEUNIT:
				mGame.moveUnit(networkAction);
				break;
			case GameConVars.NETWORK_STOPUNIT:
				mGame.stopUnit(networkAction);
				break;
			case GameConVars.NETWORK_FORCE:
//				float force = Float.parseFloat(msgs[2]);
//				mGame.getCurrentUnit().setCurrentForce(force);
				break;
			case GameConVars.NETWORK_BUYUNIT:
				mGame.addUnitNetwork(networkAction);
				break;
			case GameConVars.NETWORK_UNITANGLE:
//				float angle = Float.parseFloat(msgs[2]);
//				mGame.getCurrentUnit().getGun().setAngle(angle);
				break;
			case GameConVars.NETWORK_SHOOTUNIT:
				mGame.shootUnit(networkAction);
				break;
			case GameConVars.NETWORK_CONFIRM:
				break;
			case GameConVars.NETWORK_PING:
				break;
			case GameConVars.NETWORK_ENDTURN:
				mGame.endTurn(networkAction);
				break;
			case GameConVars.NETWORK_QUIT:
				Logger.popUp("Opponent has quit!");
				mServer.Stop();
				mClient.Disconnect();
				mGame.onMenuButton();//TODO: need to figure out game reset steps, also wait 30secs or so for reconnection
				break;
			case GameConVars.NETWORK_ERROR:
			default:
				Logger.popUp("Network Error!");
				break;
			}
			Logger.log("[ConnectionManager] Received: " + msg);

		} catch (JSONException e) {
			Logger.e(e);
		}
	}
	

	public boolean isNetworkAvailable() {
		ConnectivityManager cm = (ConnectivityManager) mActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo networkInfo = cm.getActiveNetworkInfo();
		// if no network is available networkInfo will be null, otherwise check if we are connected
		return networkInfo != null && networkInfo.isConnected();
	}

	public void close(){
		if(isConnected){
			send(GameConVars.NETWORK_QUIT + ":");
		}
		mServer.Stop();
		mClient.Disconnect();
	}

	public void setConnectedClientIP(String ip){ 	
		ip = ip.substring(1);
		boolean success = connect(ip, Server.SERVER_PORT);
		Logger.popUp(ip + " : has connected to me, trying to connect back: " + success);
		if(success){
			isConnected = true;
			Message msg = Message.obtain();
			msg.arg2 = 2;//TODO:  2 == network!
			mActivity.getMenu().getHandler().sendMessage(msg);
		}
	}

	public Client getClient(){ return mClient; }
	public boolean isConnected(){ return isConnected; }

	public Handler getHandler(){ return handler; }
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			switch(msg.what){
			case 1: //Message from Server received
				receive(msg.getData().getString("NetworkAction"));
				break;
			}
		}
	};
}
